/* eslint-disable import/no-cycle */
import protobufjs from 'protobufjs';
import md5 from 'react-native-md5';
import fetch from 'rnx-fetch';

import { Buffer } from 'buffer';
import { shellExec } from '../util/native';
import ground from './ground1';
import connection from './connection';
import {
  CLIENT_REQ_CMD,
  CLIENT_TYPE,
  HEADER_LENGTH,
  MSG_ID_INDEX,
  SERVER_RSP_CMD
} from './constant';
import Log from '../common/log';
import toUtf8 from '../util/toUtf8';
import { getUserInfo } from '../store/user';
import { APP_ID, APP_VERSION, DEBUG } from '../constant';
import { binary, concatenate, getView } from './actions';

interface Params {
  [key: string]: any;
}

let requestId = 1; // 请求id

let Headers = {
  'Content-Type': 'application/json',
  Token: ''
};

let clientInfo = 0;

const SALT = 'aishoo0eex7iTheeb8ieGae8lo5piequ';

const root = protobufjs.Root.fromJSON(ground);
const cPacketRoot = protobufjs.Root.fromJSON(connection);

const setClientInfo = (): void => {
  let clientInfoStr = binary(APP_ID, 8);
  const clientInfoArray = APP_VERSION.split('.');

  clientInfoArray.forEach((item: string): void => {
    clientInfoStr += binary(Number(item), 8);
  });
  clientInfo = parseInt(clientInfoStr, 2);
};

setClientInfo();

export const setHeaderToken = (token: string): void => {
  Headers = {
    ...Headers,
    Token: token
  };
};

export function Post(
  msgId: number,
  params: Params,
  limitFrequency: boolean
): Promise<any> {
  const id = requestId;
  requestId += 1;

  return new Promise<any>((resolve, reject): void => {
    Log.i(
      'request req : ',
      JSON.stringify({ msgId, msgName: MSG_ID_INDEX[msgId], ...params })
    );

    if (root && cPacketRoot && msgId) {
      const Message = root.lookupType(`beeground.${MSG_ID_INDEX[msgId]}`);
      const message = Message.create(params);
      const messageBuffer = Message.encode(message).finish();

      const body = {
        reqMsgId: msgId,
        reqMsgBody: messageBuffer, // PB编码
        token: Headers.Token
      };
      const ReqMsg = cPacketRoot.lookupType('client.CPacketReq');
      const reqMsg = ReqMsg.create(body);
      const reqBuffer = ReqMsg.encode(reqMsg).finish();

      shellExec.request(
        {
          headers: {
            ...Headers,
            requestId: `${id}`
          },
          method: 'POST',
          limitFrequency,
          body: Array.from(reqBuffer)
        },
        (resp: any): void => {
          const res = JSON.parse(resp);
          const RspMsg = cPacketRoot.lookupType('client.CPacketRsp');
          const rspMsg: any = RspMsg.decode(new Uint8Array(res));

          const { code, rspMsgId, rspMsgBody, errMsg, traceId } = rspMsg;

          let rspData: any;

          if (code === 0) {
            Log.i(
              'request rspMsgId : ',
              JSON.stringify({
                rspMsgId,
                msgName: MSG_ID_INDEX[rspMsgId]
              })
            );
            const RspData = root.lookupType(
              `beeground.${MSG_ID_INDEX[Number(rspMsgId)]}`
            );
            rspData = RspData.decode(new Uint8Array(rspMsgBody));
          }

          const resolveData = {
            code,
            msg: errMsg,
            data: rspData,
            requestId: id,
            traceId
          };

          Log.i(
            'request rsp : ',
            JSON.stringify({
              rspMsgId,
              msgName: MSG_ID_INDEX[rspMsgId],
              ...resolveData
            })
          );

          console.log(
            'request rsp : ',
            JSON.stringify({
              rspMsgId,
              msgName: MSG_ID_INDEX[rspMsgId],
              ...resolveData
            })
          );

          resolve(resolveData);
        },
        (msg: string): void => {
          reject(JSON.stringify({ msg, requestId: id }));
        }
      );
    }
  });
}

export function PostByShortConnection(
  reqMsgId: number,
  reqMsgBody: { [key: string]: any }
): Promise<any> {
  const id = requestId;
  requestId += 1;

  return new Promise<any>((resolve, reject): void => {
    const { userId, token } = getUserInfo();
    const url = DEBUG
      ? 'https://devapi.mifengxiaoban.com/h'
      : 'https://api.mifengxiaoban.com/h';

    const header = new ArrayBuffer(HEADER_LENGTH);
    const dataView = new DataView(header);

    dataView.setUint32(0, HEADER_LENGTH);
    dataView.setUint32(4, clientInfo);
    dataView.setUint32(8, CLIENT_REQ_CMD);
    dataView.setUint32(12, 0);

    Log.i(
      'post request ',
      JSON.stringify({
        reqMsgId,
        msgName: MSG_ID_INDEX[reqMsgId],
        reqMsgBody,
        token
      })
    );

    const proto = MSG_ID_INDEX[reqMsgId];
    const Message = root.lookupType(`beeground.${proto}`);
    const message = Message.create(reqMsgBody);
    const messageBuffer = Message.encode(message).finish();
    const body = {
      reqMsgId,
      reqMsgBody: messageBuffer, // PB编码
      token,
      clientType: CLIENT_TYPE.App,
      userId
    };

    const ReqMsg = cPacketRoot.lookupType('client.CPacketReq');
    const reqMsg = ReqMsg.create(body);
    const reqBuffer = ReqMsg.encode(reqMsg).finish();

    dataView.setUint32(16, reqBuffer.length);

    shellExec.requestByShortConnection(
      url,
      {
        headers: {
          ...Headers,
          requestId: `${id}`
        },
        method: 'POST',
        body: Array.from(
          concatenate(Int8Array, new Uint8Array(dataView.buffer), reqBuffer)
        )
      },
      (resp: any): void => {
        try {
          // base64 -> ArrayBuffer
          const res = Buffer.from(resp, 'base64');

          let receive: any[] = [];
          receive = receive.concat(Array.from(new Uint8Array(res)));

          if (receive.length < HEADER_LENGTH) {
            return;
          }

          // 获取CmdId
          const cmdId = getView(receive).getUint32(8);
          // 服务器响应包
          if (cmdId === SERVER_RSP_CMD) {
            const bytes: any = receive.slice(HEADER_LENGTH);
            const RspMessage = cPacketRoot.lookupType('client.CPacketRsp');
            const rspMessage: any = RspMessage.decode(bytes);

            if (!rspMessage) {
              Log.i(
                'post response failed ',
                JSON.stringify({
                  reqMsgId,
                  msgName: MSG_ID_INDEX[reqMsgId],
                  reqMsgBody,
                  token,
                  rspMsgId: cmdId
                })
              );
              reject(res);
            }

            const { code, rspMsgId, rspMsgBody, errMsg, traceId } = rspMessage;

            let rspBodyMessage = null;

            if (rspMsgBody.length) {
              const RspBodyMessage = root.lookupType(
                `beeground.${MSG_ID_INDEX[rspMsgId]}`
              );
              rspBodyMessage = RspBodyMessage.decode(rspMsgBody);
            }

            const resolveData = {
              code,
              msg: errMsg,
              data: rspBodyMessage,
              traceId
            };

            Log.i(
              'post response ',
              JSON.stringify({
                ...resolveData,
                reqMsgId,
                msgName: MSG_ID_INDEX[reqMsgId]
              })
            );
            resolve(resolveData);
          }
        } catch (error) {
          Log.e(
            'ShellExec',
            JSON.stringify({
              path: 'json parse error shellExec resp error',
              reason: JSON.stringify({
                url,
                error: `${error}`,
                resp: `${resp}`
              })
            })
          );
        }
      },
      (msg: any): void => {
        Log.i(
          'post response error',
          JSON.stringify({
            msg
          })
        );
        reject(JSON.stringify({ msg, requestId: id }));
      }
    );
  });
}

export function Report(url: string, content: string): Promise<any> {
  const signature = md5.hex_md5(toUtf8(content) + SALT);

  return fetch(url, {
    method: 'POST',
    headers: Headers,
    body: JSON.stringify({
      content,
      signature
    })
  });
}

export const post = (url: string, content?: string): Promise<any> => {
  return fetch(url, {
    method: 'POST',
    headers: Headers,
    body: JSON.stringify({
      content
    })
  });
};
